/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.tabbedproperties.composites;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.uml2.uml.ClassifierTemplateParameter;
import org.eclipse.uml2.uml.ConnectableElementTemplateParameter;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.OperationTemplateParameter;
import org.eclipse.uml2.uml.TemplateBinding;
import org.eclipse.uml2.uml.TemplateParameter;
import org.eclipse.uml2.uml.TemplateParameterSubstitution;
import org.eclipse.uml2.uml.TemplateSignature;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.commands.MoveInListCommand;
import com.cea.papyrus.core.commands.factories.UMLDi2ElementDeleteFactory;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.editpart.IUMLElementEditPart;
import com.cea.papyrus.core.editpart.connection.TagDisplayEditPart;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.classdiagram.commands.TemplateParameterSubstitutionCreateCommand;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.umlutils.Element;


/**
 * Composite that displays the parameters of a behavior.
 * 
 * @author Remi SCHNEKENBURGER
 */
public class TemplateBindingComposite extends DecoratedTableComposite implements ISectionComposite {

	private final static String TEMPLATE_PARAMETER_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"TemplateParameter.gif" ;
	private final static String CLASSIFIER_TEMPLATE_PARAMETER_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"ClassifierTemplateParameter.gif" ;
	private final static String OPERATION_TEMPLATE_PARAMETER_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"OperationTemplateParameter.gif" ;
	private final static String CONNECTABLE_ELEMENT_TEMPLATE_PARAMETER_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"ConnectableElementTemplateParameter.gif" ;
	private final static String TEMPLATE_PARAMETER_SUBSTITUTION_IMAGE = PapyrusPlugin.UML_ICONS_16x16+"TemplateParameterSubstitution.gif";
	
	/**
	 * key to access UML Data from table items.
	 */ 
	private final static String KEY = "parameter_data";
	
	private TabbedPropertySheetPage tabbedPropertySheetPage = null ;
	
	/**
	 * graph node of the template binding for wich template parameter substitutions are displayed.
	 */
	private GraphNode templateBindingGraphNode;
	
	/**
	 * Template binding for wich template parameter substitutions are displayed.
	 */
	private TemplateBinding templateBinding;
	
	/**
	 * Template binding for wich template parameter substitutions are displayed.
	 */
	private IUMLElementEditPart graphicalElement;
	
//	/**
//	 * Template signature for wich template parameter substitutions are defined.
//	 */
//	private TemplateSignature templateSignature;

	/**
	 * The templateable element owning the template signature for wich parameters are displayed
	 */
//	private TemplateableElement templateableElement;
	
//	/**
//	 * 
//	 */
	private Menu menu;
//	
	/**
	 * 
	 */
	private List<MenuItem> menuItems;
	
	/**
	 * 
	 */
	private List<SelectionListener> selectionListeners;

	
	/**
	 * Default Constructor.
	 */
	public TemplateBindingComposite() {
		super("Template binding");
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#createContent(org.eclipse.swt.widgets.Composite, org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory)
	 */
	/**
	 * 
	 * 
	 * @param factory 
	 * @param parent 
	 * 
	 * @return 
	 */
	public Composite createContent(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage) {
		Composite composite = super.createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
		this.tabbedPropertySheetPage = tabbedPropertySheetPage ;
//		// add an additional menu to the add Button
//		createMenuForAddButton();
		
		return composite;
	}
		
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#setElement(com.cea.utils.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 */
	@Override
	public void setElement(Element element) {
		super.setElement(element);
		this.templateBinding = (TemplateBinding)element.getUml2Element() ;
//		this.templateSignature = templateBinding.getSignature() ;
	}
	
	public void setGraphNode(GraphNode graphNode) {
		this.templateBindingGraphNode = graphNode ;
	}
	
	public void setGraphicalElement(IUMLElementEditPart graphicalElement) {
		this.graphicalElement = graphicalElement ;
	}
	
	/**
	 * Adds an additional menu to the addButton. So user can choose which element to create
	 */
	private void createMenuForAddButton() {
		menu = new Menu (addButton);
		menu.setEnabled(false) ;
		menuItems = new ArrayList<MenuItem>() ;
		selectionListeners = new ArrayList<SelectionListener>() ;
		// add an item for each formal parameter that is not yet bound to an actual value
		for (Iterator i = templateBinding.getSignature().getParameters().iterator() ; i.hasNext() ; ) {
			TemplateParameter currentFormalParameter = (TemplateParameter)i.next() ;
			// ckecks if this parameter has already been bound
			boolean found = false ;
			for (Iterator j = templateBinding.getParameterSubstitutions().iterator() ; j.hasNext() && !found; ) {
				TemplateParameterSubstitution currentSubstitution = (TemplateParameterSubstitution)j.next() ;
				if (currentSubstitution.getFormal() == currentFormalParameter)
					found = true ;
			}
			if (!found) {// currentFormalParameter has not been bound yet
				if (templateBinding.getBoundElement().isTemplate()) {
					TemplateSignature boundElementSignature = templateBinding.getBoundElement().getOwnedTemplateSignature() ;
					if (boundElementSignature.getParameters().contains(currentFormalParameter))
						found = true ;
				}
			}
			if (!found) {//
				menu.setEnabled(true) ;
				menuItems.add(new MenuItem(menu, SWT.PUSH)) ;
				MenuItem item = menuItems.get(menuItems.size()-1) ;
				if (currentFormalParameter instanceof ClassifierTemplateParameter)
					item.setImage(PapyrusPlugin.getImage(CLASSIFIER_TEMPLATE_PARAMETER_IMAGE)) ;
				else if (currentFormalParameter instanceof OperationTemplateParameter)
					item.setImage(PapyrusPlugin.getImage(OPERATION_TEMPLATE_PARAMETER_IMAGE)) ;
				else if (currentFormalParameter instanceof ConnectableElementTemplateParameter)
					item.setImage(PapyrusPlugin.getImage(CONNECTABLE_ELEMENT_TEMPLATE_PARAMETER_IMAGE)) ;
				else
					item.setImage(PapyrusPlugin.getImage(TEMPLATE_PARAMETER_IMAGE)) ;
				if (currentFormalParameter.getParameteredElement() != null)
					item.setText(((NamedElement)currentFormalParameter.getParameteredElement()).getName()) ;
				else
					item.setText("<UNDEFINED>") ;
				menuItems.get(menuItems.size()-1).addSelectionListener(new AddElementSelectionListener(org.eclipse.uml2.uml.TemplateParameterSubstitution.class, templateBinding, currentFormalParameter)) ;
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#refresh()
	 */
	/**
	 * 
	 */
	@Override
	public void refresh() {
		if( !table.isDisposed()&& (templateBinding!=null)){
			// Remove parameters from table 
			table.removeAll();

			// Read and add template parameter substitution
			Iterator<org.eclipse.uml2.uml.TemplateParameterSubstitution> it = templateBinding.getParameterSubstitutions().iterator();
			while (it.hasNext()) {
				com.cea.papyrus.umlutils.TemplateParameterSubstitution templateParameterSubstitution = new com.cea.papyrus.umlutils.TemplateParameterSubstitution(it.next());
				TableItem item = new TableItem(table, SWT.BORDER);
				item.setImage(PapyrusPlugin.getImage(TEMPLATE_PARAMETER_SUBSTITUTION_IMAGE)) ;
				item.setData(KEY, templateParameterSubstitution);
				item.setText(templateParameterSubstitution.getLabel());
			}
			if (graphicalElement != null) {
				if (graphicalElement instanceof TagDisplayEditPart) {
					((TagDisplayEditPart)graphicalElement).refresh() ;
				}
			}
		}
	}
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#addButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void addButtonPressed() {		
		// add an additional menu to the add Button
		createMenuForAddButton();

		menu.setVisible(true);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#downButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void downButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		int[] newSelection = new int[tableItems.length] ;
		for(int i = (tableItems.length-1); i >= 0; i--) {
			// get parameter substitutions list
			List parameterSubstitutions = templateBinding.getParameterSubstitutions();
			// get parameter substitution
			org.eclipse.uml2.uml.TemplateParameterSubstitution parameterSubstitution = ((com.cea.papyrus.umlutils.TemplateParameterSubstitution) tableItems[i].getData(KEY)).getUml2TemplateParameterSubstitution();
			int newIndex = table.indexOf(tableItems[i]) + 1;
			if(newIndex < parameterSubstitutions.size()) {
				//new MoveInListCommand(parameters, parameter, newIndex).execute();
				newSelection[i] = newIndex ;
				compoundCommand.add(new MoveInListCommand(parameterSubstitutions, parameterSubstitution, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
//		table.setFocus() ;
//		table.setSelection(newSelection) ;
	}

	
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#editItem(org.eclipse.swt.widgets.TableItem)
	 */
	/**
	 * 
	 * 
	 * @param item 
	 */
	@Override
	public void editItem(TableItem item) {
		if (item == null) {
			return;
		}
		com.cea.papyrus.umlutils.TemplateParameterSubstitution currentTemplateParameterSubstitution = (com.cea.papyrus.umlutils.TemplateParameterSubstitution)item.getData(KEY) ;
		if (currentTemplateParameterSubstitution!=null) {
			EditPart selection = null ;
			for (Iterator<EditPart> i=LookForElement.getAllOutlineEditParts().iterator() ; i.hasNext() ; ) {
				EditPart current = i.next() ;
				if (current.getModel() == currentTemplateParameterSubstitution.getUml2Element())
					selection = current ;
			}
			if (selection != null)
				tabbedPropertySheetPage.selectionChanged(null, new StructuredSelection(selection)) ;
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#removeButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void removeButtonPressed() {
		CompoundCommand command = new CompoundCommand(); 
		// get selection and delete it
		TableItem[] tableItems = table.getSelection();
		for(int i = 0; i < tableItems.length ; i++) {
			// get parameter
			com.cea.papyrus.umlutils.TemplateParameterSubstitution parameterSubstitution = (com.cea.papyrus.umlutils.TemplateParameterSubstitution) tableItems[i].getData(KEY);
			command.add(UMLDi2ElementDeleteFactory.eINSTANCE.createCommand(parameterSubstitution.getUml2TemplateParameterSubstitution()));
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(command);
		refresh() ;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#upButtonPressed()
	 */
	/**
	 * 
	 */
	@Override
	public void upButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		List parameterSubstitutions = templateBinding.getParameterSubstitutions();
		for(int i = 0; i < tableItems.length ; i++) {
			// get parameter
			org.eclipse.uml2.uml.TemplateParameterSubstitution parameterSubstitution = ((com.cea.papyrus.umlutils.TemplateParameterSubstitution) tableItems[i].getData(KEY)).getUml2TemplateParameterSubstitution();
			int newIndex = table.indexOf(tableItems[i]) - 1;
			if (newIndex >= 0) {
				compoundCommand.add(new MoveInListCommand(parameterSubstitutions, parameterSubstitution, newIndex));
			}
		}
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
		
	}
	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener {

		/**
		 * 
		 */
		public Class elementClass;
		
		/**
		 * 
		 */
		private TemplateBinding parentTemplateBinding ;

		/**
		 * 
		 */
		private TemplateParameter formalParameter;

				
		public AddElementSelectionListener(Class elementClass, TemplateBinding parentTemplateBinding, TemplateParameter formalParameter) {
			this.elementClass = elementClass ;
			this.parentTemplateBinding = parentTemplateBinding ;
			this.formalParameter = formalParameter ;
		}
		
		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetSelected(SelectionEvent e) {
			Command command ;
			CommandStack stack = CommandStackUtils.getCommandStack();
			command = new TemplateParameterSubstitutionCreateCommand(elementClass, parentTemplateBinding, formalParameter, null) ;
			Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
			stack.execute(command);
			refresh() ;
		}
	}
}
